#ifndef __MY_SERVICE__
#define __MY_SERVICE__

#include "data.hpp"
#include "httplib.h"
#include <iostream>

extern cloud::DataManager* _data;
namespace cloud
{
    class Service
    {
    private:
        int _server_port;
        std::string _server_ip;
        std::string _download_prefix;
        httplib::Server _server;
    private:
        //上传文件
        static void Upload(const httplib::Request& req, httplib::Response& rsp)
        {
            //post /upload
            auto ret = req.has_file("file");//判断有没有上传的文件区域
            if(ret == false)
            {
                rsp.status = 400;
                rsp.set_content("Bad Request: No file uploaded", "text/plain");
                return;
            }
            const auto& file = req.get_file_value("file");
            std::string back_dir = Config::GetInstance()->GetBackDir();

            //拿到request请求中的实际文件路径
            std::string realpath = back_dir + FileUtil(file.filename).FileName();
            FileUtil fu(realpath);
            fu.SetContent(file.content);//将数据（客户端上传的文件正文）写入文件中
            BackupInfo info;
            info.NewBackupInfo(realpath);//组织备份文件信息
            _data->Insert(info);//向数据管理模块添加备份的文件信息
            return;
        }
        static std::string TimetoStr(time_t t)
        {
            std::string tmp = std::ctime(&t);
            return tmp;
        }
        //展示页面
        static void ListShow(const httplib::Request& req, httplib::Response& rsp)
        {
            //1.获取所有的文件备份信息
            std::vector<BackupInfo> arry;
            _data->GetAll(&arry);
            //2.根据所有的备份信息,组织html文件数据
            std::stringstream ss;
            ss << "<html><head><title>Download</title></head>";
            ss << "<body><h1>Download</h1><table>";
            //遍历所有的备份文件
            for(auto& a : arry)
            {
                ss << "<tr>";
                std::string filename = FileUtil(a.real_path).FileName();
                //url+最后一次修改时间+文件大小
                ss << "<td><a href='" << a.url << "'>" << filename << "</a></td>";
                ss << "<td align='right'>" << TimetoStr(a.mtime) << "</td>";
                ss << "<td align='right'>" << a.fsize / 1024 << "k</td>";
                ss << "</tr>";
            }
            ss << "</table></body></html>";

            rsp.body = ss.str();
            rsp.set_header("Content-Type", "text/html");
            rsp.status = 200;
            return;
        }
        //一个资源的唯一标识
        //客户端第一次下载，会收到这个响应信息
        //第二次下载，就会将这个信息发送给服务器，服务器可以根据这个唯一标识判断这个资源有没有被修改过
        //如果没有修改过，直接使用原先缓存的数据，不用重新下载了
        //不仅仅是缓存用到，断点续传也会用到，因为断点续传也要保证文件没有被修改
        static std::string GetETag(const BackupInfo& info)
        {
            //etg: filename-fsize-mtime
            FileUtil fu(info.real_path);
            std::string etag = fu.FileName();
            etag += "-";
            etag += std::to_string(info.fsize);
            etag += "-";
            etag += std::to_string(info.mtime);
            return etag;
        }
        //文件下载处理函数
        static void Download(const httplib::Request& req, httplib::Response& rsp)
        {
            //1.获取客户端请求的资源路径path req.path
            //2.根据客户端请求的资源路径，获取文件备份信息
            BackupInfo info;
            _data->GetOneByURL(req.path,&info);

            //3.判断文件是否被压缩，如果被压缩，要先解压缩
            if(info.pack_flag == true)
            {
                FileUtil fu(info.pack_path);
                fu.UnCompress(info.real_path);//将文件解压到backdir备份目录下
                //4.删除压缩包，修改备份信息(已经没有被压缩)
                fu.Remove();
                info.pack_flag = false;
                _data->Update(info);
            }
            
            //读取文件数据，放入rsp.body中响应给客户端
            FileUtil fu(info.real_path);
            std::string old_etag;
            //请求中如果有If-Range字段，代表是断点续传的请求
            if(req.has_header("If-Range"))
            {
                old_etag = req.get_header_value("If-Range");
                //有If-Range字段，且这个字段的值与请求文件的最新etag一致，则符合断点续传
                //否则代表文件被修改，需要重新传
                if(old_etag == GetETag(info))
                {
                    //httplib内部实现了对于区间请求，也就是断点续传请求的处理
                    //只需要我们用户将文件所有数据读取到rsp.body中，它内部会自动根据请求区间
                    //从body中取出指定区间数据进行响应
                    //std::string range = req.get_header_value("Range");  bytes=start=end
                    fu.GetContent(&rsp.body);
                    //6.设置响应头部字段,Etag, Accpt-Ranges: bytes
                    rsp.set_header("Accept-Ranges", "Bytes");//告诉客户端支持断点续传
                    rsp.set_header("ETag", GetETag(info));
                    rsp.set_header("Content-Type", "application/octet-stream");
                    //rsp.set_header("Content-Range", "bytes start-end/fsize")//httplib会自动设置

                    rsp.status = 206;//区间请求响应的是206
                    return;
                }
            }
          
            //如果没有If-Range字段，则是正常下载,或者如果有这个字段，但是他的值与当前文件的etag
            //不一致，则也必须重新返回全部数据
            fu.GetContent(&rsp.body);
            //6.设置响应头部字段,Etag, Accpt-Ranges: bytes
            rsp.set_header("Accept-Ranges", "Bytes");
            rsp.set_header("ETag", GetETag(info));
            rsp.set_header("Content-Type", "application/octet-stream");
            rsp.status = 200;
            return;
        }
    public:
        Service(){
            Config* config = Config::GetInstance();
            _server_port = config->GetServerPort();
            _server_ip = config->GetServerIp();
            _download_prefix = config->GetDownloadPrefix();
        }
        bool RunModule()
        {
            _server.Post("/upload", Upload);
            _server.Get("/listshow",ListShow);
            _server.Get("/",ListShow);
            std::string download_url = _download_prefix + "(.*)";
            _server.Get(download_url, Download);

            _server.listen(_server_ip.c_str(),_server_port);

            return true;
        }
    };
}

#endif
